package com.cdty.chess.service.impl;

import com.cdty.chess.controller.MyWebSocketHandler;
import com.cdty.chess.model.*;
import com.cdty.chess.service.BetInfoService;
import com.cdty.chess.vo.BetInfoSticVo;
import com.cdty.chess.vo.BetInfoVo;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.jdbc.core.namedparam.BeanPropertySqlParameterSource;
import org.springframework.jdbc.core.namedparam.MapSqlParameterSource;
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;

import java.math.BigDecimal;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.time.LocalDateTime;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;

@Service
public class BetInfoServiceImpl implements BetInfoService {

    @Autowired
    private MyWebSocketHandler webSocketHandler;

    @Autowired
    SseEmitterManager manager;

    // 使用ConcurrentHashMap存储每个ID的剩余额度
    private final ConcurrentHashMap<String, BigDecimal> idLimits = new ConcurrentHashMap<>();


    // 初始化所有ID的限额
    public void initializeLimit() {
        String sql = """
                    select * from chess_info  
                """;
        List<ChessInfo> list = template.query(sql, new BeanPropertyRowMapper<>(ChessInfo.class));
        for (int i = 0; i < list.size(); i++) {
            idLimits.put(list.get(i).getId(), list.get(i).getScore());
        }
    }

    @Autowired
    NamedParameterJdbcTemplate template;


    @Override
    @Transactional
    public String addBetInfo(BetInfo info) {
        //先查询这一局的状态是否可下注状态
        String sql = """
                    select * from bet_count_info a where a.id = :bcid
                """;
        BetCountInfo rinfo = template.queryForObject(sql, new MapSqlParameterSource("bcid", info.getBcid()), new RowMapper<BetCountInfo>() {
            @Override
            public BetCountInfo mapRow(ResultSet rs, int rowNum) throws SQLException {
                BetCountInfo result = new BetCountInfo();
                result.setId(rs.getString("id"));
                result.setIs_bet(rs.getString("is_bet"));
                result.setN_order(rs.getInt("n_order"));
                return result;
            }
        });
        if (rinfo != null && "4".equals(rinfo.getIs_bet())) {
            return "此局已开奖,请等待下一局";
        }
        if (rinfo != null && "1".equals(rinfo.getIs_bet())) {
            return "此局未开盘,请等待管理员开盘";
        }
        if (rinfo != null && "3".equals(rinfo.getIs_bet())) {
            return "此局已封盘,请等待下一局";
        }
        //查询用户的余额
        String balancesql = """
                    select * from cuser where  uid=:uid
                """;
        Cuser cuser = template.queryForObject(balancesql, new MapSqlParameterSource("uid", info.getUid()), new RowMapper<Cuser>() {
            @Override
            public Cuser mapRow(ResultSet rs, int rowNum) throws SQLException {
                Cuser result = new Cuser();
                result.setUid(rs.getString("uid"));
                result.setBalance(rs.getBigDecimal("balance"));
                result.setUname(rs.getString("uname"));
                return result;
            }
        });
        if (info.getBet_amount().compareTo(cuser.getBalance()) == 1) {
            return "余额不足请充值";
        }
        if(idLimits.size()==0){
            this.initializeLimit();
        }
        //判断主字是否超上限
        if(StringUtils.hasText(info.getSingle_main())){
            String[] main = info.getSingle_main().split(",");
            String[] main_name = info.getSingle_main_name().split(",");
            for (int i = 0; i < main.length; i++) {
                BigDecimal riid = idLimits.get(main[i]);
                if (riid.compareTo(info.getBet_amount().divide(new BigDecimal(main_name.length),1,BigDecimal.ROUND_HALF_UP)) == -1) {
                    return "该局选择的主字[" + main_name[i] + "]剩余额度为:"+riid;
                }
            }
        }
        //判断回本字是否超上限
        if (StringUtils.hasText(info.getRecovery_other())) {
            String[] other = info.getRecovery_other().split(",");
            String[] other_name = info.getRecovery_other_name().split(",");
            for (int i = 0; i < other.length; i++) {
                BigDecimal riid = idLimits.get(other[i]);
                if (riid.compareTo(info.getBet_amount().divide(new BigDecimal(other.length),1,BigDecimal.ROUND_HALF_UP)) == -1) {
                    return "该局选择的回本字[" + other_name[i] + "]剩余额度为:"+riid;
                }
            }
        }
        //扣减 主字
        if(StringUtils.hasText(info.getSingle_main())){
            String[] main = info.getSingle_main().split(",");
            String[] main_name = info.getSingle_main_name().split(",");
            for (int i = 0; i < main.length; i++) {
                BigDecimal riid = idLimits.get(main[i]);
                idLimits.put(main[i],riid.subtract(info.getBet_amount().divide(new BigDecimal(main_name.length),1,BigDecimal.ROUND_HALF_UP)));
            }
        }
        //idLimits.put(info.getSingle_main(),biid.subtract(info.getBet_amount().multiply(info.getOdds()).multiply(new BigDecimal("0.1"))));
        //扣减 回本字
        if (StringUtils.hasText(info.getRecovery_other())) {
            String[] other = info.getRecovery_other().split(",");
            String[] other_name = info.getRecovery_other_name().split(",");
            for (int i = 0; i < other.length; i++) {
                BigDecimal riid = idLimits.get(other[i]);
                idLimits.put(other[i],riid.subtract(info.getBet_amount().divide(new BigDecimal(other.length),1,BigDecimal.ROUND_HALF_UP)));
            }
        }
        try {
            // 查询主键
            String btsql = """
                        insert into bet_info (
                        	uid,bcid,single_type,single_main,recovery_type,recovery_main,recovery_other,bet_amount,create_time,bet_des
                        	)values(
                        		:uid,:bcid,:single_type,:single_main,:recovery_type,:recovery_main,:recovery_other,:bet_amount,now(),:bet_des
                        	)
                    """;
            String m = info.getOname()+"[" + info.getSingle_main_name()+"]";
            if(StringUtils.hasText(info.getRecovery_other())){
                m+="和回本字["+info.getRecovery_other_name()+"]";
            }
            int update = this.template.update(btsql, new MapSqlParameterSource("uid", info.getUid())
                    .addValue("bcid", info.getBcid())
                    .addValue("single_type", info.getSingle_type())
                    .addValue("single_main", info.getSingle_main())
                    .addValue("recovery_type", info.getRecovery_type())
                    .addValue("recovery_main", info.getRecovery_other())
                    .addValue("recovery_other", info.getRecovery_other())
                    .addValue("bet_amount", info.getBet_amount())
                    .addValue("bet_des",m)
            );
            if (update == 1) {
                //更新用户表余额
                String upacountsql = """
                            update cuser set balance =:balance,up_time = now() where uid=:uid
                        """;
                BigDecimal bal = cuser.getBalance().subtract(info.getBet_amount());
                this.template.update(upacountsql, new MapSqlParameterSource("balance", bal)
                        .addValue("uid", info.getUid()));
                //创建结构化消息对象发送消息到前端
                Map<String, String> messageObj = new HashMap<>();

                messageObj.put("content", cuser.getUname()+":第"+rinfo.getN_order()+"局下注"+m+"共:"+info.getBet_amount());
                try {
                    ObjectMapper mapper = new ObjectMapper();
                    String msg = mapper.writeValueAsString(Map.of(
                            "event", "BetMasterAndUid",
                            "data",messageObj
                    ));
                    //保存消息
                    String msql = """
                        INSERT INTO message_queue 
                        (id, event_type, content, target_uid, is_broadcast, is_master_only, status)
                        VALUES 
                        (:id, :event_type, :content, :target_uid, :is_broadcast, :is_master_only, :status)
                        """;
                    //持久化消息
                    MessageQueue queue = new MessageQueue();
                    queue.setId(UUID.randomUUID().toString());
                    queue.setEvent_type("BetAllInfo");
                    queue.setIs_broadcast(true); //表示给所有人发
                    queue.setContent(msg);
                    //queue.setTarget_uid(""); //管理员都为空
                    queue.setIs_master_only(false); //只给管理员发
                    template.update(msql, new BeanPropertySqlParameterSource(queue));
                    webSocketHandler.broadcastAll(msg);
                    //查询所有下注记录  更新下注额度
                    List<BetInfoSticVo> betList = this.getBetList(info.getBcid());
                    String msgAll = mapper.writeValueAsString(Map.of(
                            "event", "BetAllInfo",
                            "data",betList
                    ));
                    webSocketHandler.broadcastAll(msgAll);
                } catch (JsonProcessingException e) {
                    // 处理异常
                }
                return "success";
            } else {
                return "下注失败";
            }
        } catch (Exception e) {
            e.getStackTrace();
        }
        return null;
    }

    @Override
    public List<BetInfo> getBetInfo(LocalDateTime s_time, LocalDateTime e_time, String uid) {
        String sql = """
                select * from bet_info where uid = :uid and create_time between :s_time and :e_time;
                """;
        MapSqlParameterSource param = new MapSqlParameterSource();
        param.addValue("uid", uid);
        param.addValue("s_time", s_time);
        param.addValue("e_time", e_time);
        List<BetInfo> info = template.query(sql, param, new BeanPropertyRowMapper<>(BetInfo.class));

        return info;
    }

    /*
    @Override
    public List<BetInfo> getBetInfo(LocalDateTime s_time, LocalDateTime e_time, String uid) {
        return mapper.getBetInfo(s_time,e_time,uid);
    }*/

    @Override
    public List<BetInfoSticVo> getBetList(String bcid) {

        String sql = """
                 select
                        	bi.*,oi.b_dtype,
                        oi.oname,oi.odds,oi.m_count,r_count
                        from
                        	bet_count_info bci
                        inner join bet_info bi on
                        	bci.id = bi.bcid
                        inner join odds_info oi on
                        	oi.b_dtype = bi.single_type
                        where  1=1 and bci.id =:bcid
                """;
        String chessSql =" select * from chess_info order by n_order";
        MapSqlParameterSource param = new MapSqlParameterSource();
        param.addValue("bcid", bcid);
        List<BetInfoVo> info = template.query(sql, param, new BeanPropertyRowMapper<>(BetInfoVo.class));
        List<ChessInfo> chessInfos = template.query(chessSql, param, new BeanPropertyRowMapper<>(ChessInfo.class));
        Map<String,BigDecimal> map = new HashMap<>();
        chessInfos.forEach(item->{
            map.put(item.getId(),new BigDecimal(0));
        });
        if(info.size()>0){
            //开始统计 统计逻辑下注金额/10*倍率就是每个主字的下注金额,回本字金额就是下注金额/10
            for (int i = 0; i < info.size(); i++) {
                String bDtype = info.get(i).getB_dtype();
                BigDecimal betAmount = info.get(i).getBet_amount()== null ? new BigDecimal(0) : info.get(i).getBet_amount();//下注金额
                //主字和回本字计算方式不同，主字是下注金额除以字个数，回本字是除以10。
                BigDecimal divide = betAmount.divide(new BigDecimal(10), 2, BigDecimal.ROUND_HALF_UP);//最小计算度
                //特殊处理三字下注
                BigDecimal divide3 = betAmount.divide(new BigDecimal(3), 2, BigDecimal.ROUND_HALF_UP);//最小计算度
                //算主字
                String[] splitmain = info.get(i).getSingle_main().split(",");
                String[] splitother = info.get(i).getRecovery_other().split(",");
                BigDecimal betEnd = divide.multiply(info.get(i).getOdds());
                for(int j=0;j<splitmain.length;j++){
                    if(bDtype.equals(OddsEnum.S003.getValue())){
                        BigDecimal spl = map.get(splitmain[j]) == null ? new BigDecimal(0) : map.get(splitmain[j]);
                        map.put(splitmain[j],spl.add(divide3));
                    }else{
                        BigDecimal spl = map.get(splitmain[j]) == null ? new BigDecimal(0) : map.get(splitmain[j]);
                        map.put(splitmain[j],spl.add(betEnd));
                    }

                }
                //算回本字
                for(int j=0;j<splitother.length;j++){
                    BigDecimal spl = map.get(splitother[j]) == null ? new BigDecimal(0) : map.get(splitother[j]);
                    map.put(splitother[j],spl.add(divide));
                }
            }
        }
        List<BetInfoSticVo> list = new ArrayList<>();
        chessInfos.forEach(item->{
            BetInfoSticVo vo = new BetInfoSticVo();
            vo.setId(item.getId());
            vo.setN_order(item.getN_order());
            vo.setChessName(item.getCname());
            vo.setSumScore(map.get(item.getId()));
            list.add(vo);
        });

        return list;
    }

    @Override
    public String getContent() {
        String sql = """
                select t_content from betcontent
                """;
        String content = this.template.queryForObject(sql, new MapSqlParameterSource(), String.class);
        return content;
    }

    @Override
    public String updateContent(String content) {
        String sql = "update betcontent set t_content = :t_content";
        this.template.update(sql, new MapSqlParameterSource("t_content", content));
        return "success";
    }
}

